package erlangtools;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class AnalysisResult {

	private Map<Integer, String> unprocessed = new HashMap<Integer, String>(); 
	private Map<Integer, String> comments = new HashMap<Integer, String>();
	private Map<Integer, String> atoms = new HashMap<Integer, String>();
	private Map<String, Integer> reverseAtoms = new HashMap<String, Integer>();
	private Map<Integer, String> strings = new HashMap<Integer, String>();
	private Map<Integer, String> vars = new HashMap<Integer, String>();
	private Map<String, Integer> reverseVars = new HashMap<String, Integer>();
	private Map<Integer, String> macros = new HashMap<Integer, String>();
	private Map<String, Integer> reverseMacros = new HashMap<String, Integer>();
	private Map<String, List<FunctionClause>> clauses = new HashMap<String, List<FunctionClause>>();
	private Map<String, List<Invocation>> invocations = new HashMap<String, List<Invocation>>();

	private Map<Integer, Element> atomsByNumber = new HashMap<Integer, Element>();
	private Map<String, Element> atomsByName = new HashMap<String, Element>();
	private Map<Integer, Element> varsByNumber = new HashMap<Integer, Element>();
	private Map<String, Element> varsByName = new HashMap<String, Element>();
	private Map<Integer, Element> macrosByNumber = new HashMap<Integer, Element>();
	private Map<String, Element> macrosByName = new HashMap<String, Element>();
	
	public Map<Integer, Element> getVarsByNumber() {
		return varsByNumber;
	}
	public Map<String, Element> getVarsByName() {
		return varsByName;
	}
	public Map<Integer, String> getVars() {
		return vars;
	}
	public Map<String, Integer> getReverseVars() {
		return reverseVars;
	}
	public Map<Integer, String> getMacros() {
		return macros;
	}
	public Map<String, Integer> getReverseMacros() {
		return reverseMacros;
	}
	public Map<Integer, String> getUnprocessed() {
		return unprocessed;
	}
	public Map<Integer, String> getComments() {
		return comments;
	}
	public Map<Integer, String> getAtoms() {
		return atoms;
	}
	public Map<String, Integer> getReverseAtoms() {
		return reverseAtoms;
	}
	public Map<Integer, String> getStrings() {
		return strings;
	}
	public Map<String, List<FunctionClause>> getClauses() {
		return clauses;
	}
	public Map<String, List<Invocation>> getInvocations() {
		return invocations;
	}
	
	public void normalize() {
		Pattern pattern = Pattern.compile("\\[\\*(atom|var|macro) line=\\d+ num=(\\d+)]");
		List<String> keys = new ArrayList<String>(clauses.keySet());
		for (String key: keys) {
			List<FunctionClause> l = clauses.get(key);
			String[] nameArity = key.split("/");
			String newKey = atoms.get(getAtomNum(nameArity[0]));
			for (FunctionClause fc: l) {
				Matcher matcher = pattern.matcher(fc.getPattern());
				StringBuffer stringedText = new StringBuffer();
				while (matcher.find()) {
					String type = matcher.group(1);
					int i = new Integer(matcher.group(2));
					String replacement;
					if (type.equals("atom")) {
						replacement = atoms.get(i);
					} else if (type.equals("var")) {
						replacement = vars.get(i);
					} else {
						replacement = macros.get(i);
					}
					matcher.appendReplacement(stringedText, replacement);
				}
				matcher.appendTail(stringedText);
				fc.setPattern(stringedText.toString());
			}
			clauses.remove(key);
			clauses.put(newKey + "/" + nameArity[1], l);
		}
		keys = new ArrayList<String>(invocations.keySet());
		for (String key: keys) {
			List<Invocation> l = invocations.get(key);
			String[] nameArity = key.split("/");
			String newKey = atoms.get(getAtomNum(nameArity[0]));
			for (Invocation inv: l) {
				if (inv.getModule() != null) {
					int num = getAtomNum(inv.getModule());
					inv.setModule(atoms.get(num));
				}
				int num = getAtomNum(inv.getFunction());
				inv.setFunction(atoms.get(num));
			}
			invocations.remove(key);
			invocations.put(newKey + "/" + nameArity[1], l);
		}
		List<String> atomList = new ArrayList<String>(reverseAtoms.keySet());
		for (String atom: atomList) {
			if (atom.indexOf(":") > -1) {
				String[] split = atom.split(":");
				String module = split[0];
				String fun = split[1];
				int num = reverseAtoms.get(atom);
				reverseAtoms.remove(atom);
				atoms.remove(num);
				Element oldAtomElement = atomsByName.get(atom);
				atomsByName.remove(atom);
				atomsByNumber.remove(num);
				if (!reverseAtoms.containsKey(module)) {
					int newAtom = atoms.size() + 1;
					reverseAtoms.put(module, newAtom);
					atoms.put(newAtom, module);
					Element newAtomElement = new Element();
					newAtomElement.setName(module);
					newAtomElement.getLines().addAll(oldAtomElement.getLines());
					newAtomElement.setType("atom");
					atomsByNumber.put(newAtom, newAtomElement);
					atomsByName.put(module, newAtomElement);
				} else {
					Element atomElement = atomsByName.get(module);
					atomElement.getLines().addAll(oldAtomElement.getLines());
				}
				if (!reverseAtoms.containsKey(fun)) {
					int newAtom = atoms.size() + 1;
					reverseAtoms.put(fun, newAtom);
					atoms.put(newAtom, fun);
					Element newAtomElement = new Element();
					newAtomElement.setName(fun);
					newAtomElement.getLines().addAll(oldAtomElement.getLines());
					newAtomElement.setType("atom");
					atomsByNumber.put(newAtom, newAtomElement);
					atomsByName.put(fun, newAtomElement);
				} else {
					Element atomElement = atomsByName.get(fun);
					atomElement.getLines().addAll(oldAtomElement.getLines());
				}
			}
		}
	}
	
	private int getAtomNum(String atom) {
		int i = 0;
		try {
			while (!Character.isDigit(atom.charAt(i))) i++;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return new Integer(atom.substring(i));
	}
	public Map<Integer, Element> getAtomsByNumber() {
		return atomsByNumber;
	}
	public Map<String, Element> getAtomsByName() {
		return atomsByName;
	}
	public Map<Integer, Element> getMacrosByNumber() {
		return macrosByNumber;
	}
	public Map<String, Element> getMacrosByName() {
		return macrosByName;
	}
	
}
